#ifndef CPU_ONLY

#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>

#include <ctime>

#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <glog/logging.h>

#include "caffe/util/info_log.hpp"
#include "caffe/util/device_alternate.hpp"

namespace caffe {
  namespace fs = boost::filesystem;
  using sstream = std::stringstream;
  using std::string;

  boost::thread_specific_ptr<InfoLog> tls_info_log;

  InfoLog& InfoLog::Get() {
    if (!tls_info_log.get()) {
      tls_info_log.reset(new InfoLog());
    }

    return *(tls_info_log.get());
  }

  void InfoLog::Reset() {
    tls_info_log.reset();
    return ;
  }

  InfoLog::InfoLog() {
    CUDA_CHECK(cudaGetDevice(&this->device_id_));

    this->base_log_folder_ = "./LOG/Device_" + std::to_string(this->device_id_);
    this->work_folder_ = "";

    if (!fs::exists(this->base_log_folder_) and
        !fs::create_directories(this->base_log_folder_)) {
      LOG(FATAL) << "Failed to create folder: " << this->base_log_folder_;
    }
    this->base_log_folder_ += "/";
  }

  InfoLog::~InfoLog() {
    if (log_stream_.is_open()) {
      log_stream_.close();
    }
  }

  void InfoLog::SetRootFolder(string root_folder) {
    if (this->base_log_folder_.find(root_folder) != std::string::npos) {
      return ;
    }

    this->base_log_folder_ += root_folder;

    fs::path base_dir(this->base_log_folder_);
    if (!fs::exists(base_dir) and !fs::create_directories(base_dir)) {
      LOG(FATAL) << "Failed to create folder: " << this->base_log_folder_;
    }

    this->base_log_folder_ += "/";
  }

  void InfoLog::SetWorkFolder(string work_folder) {
    auto worker_len = this->work_folder_.size();
    if (this->work_folder_.compare(0, (worker_len - 1), work_folder)) {
      return ;
    }
    this->work_folder_ = work_folder;

    fs::path log_dir(this->base_log_folder_ + this->work_folder_);
    if (!fs::exists(log_dir) and !fs::create_directories(log_dir)) {
      LOG(FATAL) << "Failed to create folder: " << this->base_log_folder_;
    }

    this->work_folder_ += "/";
  }

  void InfoLog::RecordInfoLog(string folder, string label_str, string log_type, string log_val) {
    std::replace(log_type.begin(), log_type.end(), '/', '_');

    if (this->log_stream_.is_open()) {
      this->log_stream_.close();
    }

    auto curr_log_folder = this->base_log_folder_ + this->work_folder_ + folder;
    if (!fs::exists(curr_log_folder) and !fs::create_directories(curr_log_folder)) {
      LOG(FATAL) << "Failed to create folder: " << curr_log_folder;
    }

    this->log_stream_.open(curr_log_folder + "/" + log_type + ".csv", std::ios::app);
    if (!this->log_stream_.is_open()) {
      LOG(FATAL) << "Failed to open log file: " << (curr_log_folder + log_type + ".csv");
    }

    if (!label_str.empty()) {
      this->log_stream_ << log_val;
    } else {
      this->log_stream_ << label_str << "," << this->device_id_ << "," << log_val;
    }
    this->log_stream_.flush();

    this->log_stream_.close();
  }
} // namespace caffe

#endif  // CPU_ONLY
